package solverdualization;

import problem.SingleWareHouseProblem;
import sun.print.resources.serviceui;
import uncertainty.UncertaintySet;
import util.Memorier;
import util.Timer;
import ilog.concert.IloException;
import ilog.concert.IloLinearNumExpr;
import ilog.concert.IloNumVar;
import ilog.concert.IloObjective;
import ilog.cplex.IloCplex;

public abstract class DualSolver {
	
	protected Memorier mem;
	protected Timer timer;
	protected SingleWareHouseProblem prob;
	protected UncertaintySet us;
	protected int numrest;
	protected int numvar;
	protected IloCplex cplex;
	protected IloNumVar[][] x;
	protected IloNumVar[][] s;
	protected IloNumVar[][] r;
	protected IloNumVar[][] y;
	protected IloNumVar[][] y1;
	protected IloNumVar[][] y2;
	protected IloNumVar[][] y3;
	protected IloNumVar[][] y4;
	protected double[] alpha;
	protected double[][] m;
	protected IloNumVar kappa;
	protected int dim;
	protected double gamma;
	
	protected void zeraFull(IloNumVar[][] xis, int n, int m) throws IloException{
		IloLinearNumExpr exp = cplex.linearNumExpr();
		
		for(int i = 0; i < n; i++){
			for(int j = 0; j <= m; j++){
				exp = cplex.linearNumExpr();
				exp.addTerm(1, xis[i][j]);
				cplex.addEq(0, exp);
			}
		}
	}
	
	protected void zeracol(IloNumVar xis) throws IloException{
		IloLinearNumExpr exp = cplex.linearNumExpr();
		exp.addTerm(1.0, xis);
		cplex.addEq(0.0, exp);
	}

	protected void zeraParty(IloNumVar[][] xis, int n, int m) throws IloException{
		IloLinearNumExpr exp = cplex.linearNumExpr();
		
		for(int i = 0; i < n; i++){
			for(int j = i+1; j <= m; j++){
				exp = cplex.linearNumExpr();
				exp.addTerm(1, xis[i][j]);
				cplex.addEq(0, exp);
			}
		}
	}
	
	protected void addObjFunction() throws IloException{
		IloLinearNumExpr exp =  cplex.linearNumExpr();
		for(int i = 0; i < dim; i++){
			exp.addTerm(prob.getDemandCosts().get(i), x[i][dim]);
			for(int j = 0; j < i; j++){
				exp.addTerm(prob.getDemandCosts().get(i)*alpha[j], x[i][j]);
			}
		}
		
		for(int i = 0; i < dim-1; i++){
			exp.addTerm(prob.getStorageCosts().get(i), s[i][dim]);
			exp.addTerm(prob.getShortageCosts().get(i), r[i][dim]);
			for(int j = 0; j < i; j++){
				exp.addTerm(prob.getStorageCosts().get(i)*alpha[j], s[i][j]);
				exp.addTerm(prob.getShortageCosts().get(i)*alpha[j], r[i][j]);
			}
		}
		IloObjective sobj = cplex.minimize(exp);
		cplex.add(sobj);
	}
	
	protected void addDualRest(int t) throws IloException{
		IloLinearNumExpr exp =  cplex.linearNumExpr();
		exp.addTerm(-2, y[t][dim]);
		for(int i = 0; i <= t; i++)exp.addTerm(-2*alpha[i] , y[t][i]); 
		cplex.addGe(exp,0);

		exp =  cplex.linearNumExpr();
		exp.addTerm(-2, y1[t][dim]);
		for(int i = 0; i <= t; i++)exp.addTerm(-2*alpha[i] , y1[t][i]); 
		cplex.addGe(exp,0);

		exp =  cplex.linearNumExpr();
		exp.addTerm(-2, y2[t][dim]);
		for(int i = 0; i <= t; i++)exp.addTerm(-2*alpha[i] , y2[t][i]); 
		cplex.addGe(exp,0);
		
		exp =  cplex.linearNumExpr();
		exp.addTerm(-2, y3[t][dim]);
		for(int i = 0; i <= t; i++)exp.addTerm(-2*alpha[i] , y3[t][i]); 
		cplex.addGe(exp,0);
		
		for(int i = 0; i < dim; i++){
			exp =  cplex.linearNumExpr();
			exp.addTerm(1 - alpha[i], y[t][dim]);
			for(int j = 0; j <= t; j++)exp.addTerm(alpha[i] - m[t][i] , y[t][j]); 
			cplex.addGe(exp,0);

			exp =  cplex.linearNumExpr();
			exp.addTerm(1 - alpha[i], y1[t][dim]);
			for(int j = 0; j <= t; j++)exp.addTerm(alpha[i] - m[t][i] , y1[t][j]); 
			cplex.addGe(exp,0);
			
			exp =  cplex.linearNumExpr();
			exp.addTerm(1 - alpha[i], y2[t][dim]);
			for(int j = 0; j <= t; j++)exp.addTerm(alpha[i] - m[t][i] , y2[t][j]); 
			cplex.addGe(exp,0);
			
			exp =  cplex.linearNumExpr();
			exp.addTerm(1 - alpha[i], y3[t][dim]);
			for(int j = 0; j <= t; j++)exp.addTerm(alpha[i] - m[t][i] , y3[t][j]); 
			cplex.addGe(exp,0);

		}
		
		double[] coef = new double[dim+1];
		
		for(int i = 0; i < dim; i++){
			for(int j = 0; j < dim; j++){
				coef[i] += m[i][j]; 
			}
			coef[dim] += alpha[i];
		}
		
		exp =  cplex.linearNumExpr();
		exp.addTerm(gamma - coef[dim], y[t][dim]);
		for(int j = 0; j <= t; j++)exp.addTerm(gamma*alpha[j] - coef[j] , y[t][j]); 
		cplex.addGe(exp,0);	

		exp =  cplex.linearNumExpr();
		exp.addTerm(gamma - coef[dim], y1[t][dim]);
		for(int j = 0; j <= t; j++)exp.addTerm(gamma*alpha[j] - coef[j] , y1[t][j]); 
		cplex.addGe(exp,0);	
		
		exp =  cplex.linearNumExpr();
		exp.addTerm(gamma - coef[dim], y2[t][dim]);
		for(int j = 0; j <= t; j++)exp.addTerm(gamma*alpha[j] - coef[j] , y2[t][j]); 
		cplex.addGe(exp,0);	
		
		exp =  cplex.linearNumExpr();
		exp.addTerm(gamma - coef[dim], y3[t][dim]);
		for(int j = 0; j <= t; j++)exp.addTerm(gamma*alpha[j] - coef[j] , y3[t][j]); 
		cplex.addGe(exp,0);	
	}
	
	protected void addCapacityRest(int t, int k) throws IloException{
		IloLinearNumExpr exp =  cplex.linearNumExpr();
		exp.addTerm(1.0, x[t][k]);
		exp.addTerm(1.0, y[t][k]);
		if(k == dim)cplex.addEq(prob.getLimits().get(t), exp);
		else cplex.addEq(0.0, exp);
	}
	
	protected void addNonNegative(int t) throws IloException{
		IloLinearNumExpr exp =  cplex.linearNumExpr();

		exp = cplex.linearNumExpr();
		exp.addTerm(1.0, x[t][dim]);
		exp.addTerm(1.0, y1[t][dim]);
		cplex.addEq(0.0, exp);
		
		exp = cplex.linearNumExpr();
		exp.addTerm(1.0, s[t][dim]);
		exp.addTerm(1.0, y2[t][dim]);
		cplex.addEq(0.0, exp);
		
		exp = cplex.linearNumExpr();
		exp.addTerm(1.0, r[t][dim]);
		exp.addTerm(1.0, y3[t][dim]);
		cplex.addEq(0.0, exp);
		
		for(int i = 0; i < t; i++){
			exp = cplex.linearNumExpr();
			exp.addTerm(1.0, x[t][i]);
			exp.addTerm(1.0, y1[t][i]);
			cplex.addEq(0.0, exp);
			
			exp = cplex.linearNumExpr();
			exp.addTerm(1.0, s[t][i]);
			exp.addTerm(1.0, y2[t][i]);
			cplex.addEq(0.0, exp);
			
			exp = cplex.linearNumExpr();
			exp.addTerm(1.0, r[t][i]);
			exp.addTerm(1.0, y3[t][i]);
			cplex.addEq(0.0, exp);
		}
	}
	
	protected void addStorageRest(int t, int k) throws IloException{
		IloLinearNumExpr exp =  cplex.linearNumExpr();
		exp.addTerm(-1.0, x[t][k]);
		if(t > 0 && t < dim -1){
			exp.addTerm(-1.0 , s[t-1][k]);
			exp.addTerm(1.0, s[t][k]);
			exp.addTerm(1.0, r[t-1][k]);
			exp.addTerm(-1.0, r[t][k]);
		}
		
		if(t==0){
			exp.addTerm(1.0, s[t][k]);
			exp.addTerm(-1.0, r[t][k]);	
		}
	
		if(t==dim-1){
			exp.addTerm(-1.0, s[t-1][k]);
			exp.addTerm(1.0, r[t-1][k]);
		}
		
		if(k == dim)cplex.addEq(-us.getMedians().get(t), exp);
		else if(k == t)cplex.addEq(-us.getDeviations().get(t), exp);
		else cplex.addEq(0.0, exp);
	}
	
	public DualSolver(SingleWareHouseProblem p, UncertaintySet set) throws IloException {
		mem = new Memorier();
		timer = new Timer();
		numvar = 0;
		numrest = 0;
		prob = p;
		us = set;
		cplex = new IloCplex();
		dim = p.getDim();
		p = prob;
		RestMatrix ret = new RestMatrix(dim, set);
		m = ret.getMatrix();
		gamma = set.getGamma();
		
		x = new IloNumVar[dim][dim+1];
		y = new IloNumVar[dim][dim+1];
		y1 = new IloNumVar[dim][dim+1];
		y2 = new IloNumVar[dim][dim+1];
		y3 = new IloNumVar[dim][dim+1];
		s = new IloNumVar[dim][dim+1];
		r = new IloNumVar[dim][dim+1];
		
		alpha = ret.getVector();
		
		for(int i =0; i < dim; i++){
			for(int j = 0; j <= dim; j++){
				s[i][j] = cplex.numVar(-Double.MAX_VALUE, Double.MAX_VALUE,"s["+Integer.toString(i)+"]["+ Integer.toString(j)+"]");
				y[i][j] = cplex.numVar(-Double.MAX_VALUE, Double.MAX_VALUE,"y["+Integer.toString(i)+"]["+ Integer.toString(j)+"]");
				y1[i][j] = cplex.numVar(-Double.MAX_VALUE, Double.MAX_VALUE,"y1["+Integer.toString(i)+"]["+ Integer.toString(j)+"]");
				y2[i][j] = cplex.numVar(-Double.MAX_VALUE, Double.MAX_VALUE,"y2["+Integer.toString(i)+"]["+ Integer.toString(j)+"]");
				y3[i][j] = cplex.numVar(-Double.MAX_VALUE, Double.MAX_VALUE,"y3["+Integer.toString(i)+"]["+ Integer.toString(j)+"]");
				x[i][j] = cplex.numVar(-Double.MAX_VALUE, Double.MAX_VALUE,"x["+Integer.toString(i)+"]["+ Integer.toString(j)+"]");
				r[i][j] = cplex.numVar(-Double.MAX_VALUE, Double.MAX_VALUE,"r["+Integer.toString(i)+"]["+ Integer.toString(j)+"]");
			}
		}

	}

	public abstract void builModel() throws IloException;
	
	public double solve() throws IloException{
		cplex.setParam(IloCplex.BooleanParam.PreInd, false);
		timer.start();
		cplex.exportModel("model.lp");
		if(!cplex.solve()) System.out.println("Problems with the Solution!");
		cplex.writeSolution("solution.txt");
		cplex.writeBasis("basis.txt");
		timer.stop();
		return cplex.getObjValue();
	}

	public double getTime() {
		return timer.getTimeSec();
	}

}
